#ifndef _DBBDELEGATE_H_
#define _DBBDELEGATE_H_

#include "csType.h"

template<class ArgType> class csDelegate
{
public:
	/// constructor
	csDelegate();
	/// invokation operator
	void operator()(ArgType arg) const;
	/// setup a new delegate from a method call
	template<class T, void (T::*method)(ArgType)> static		csDelegate<ArgType> FromMethod(T* objPtr);
	/// setup a new delegate from a function call
	template<void(*function)(ArgType)> static					csDelegate<ArgType> FromFunction();

	bool operator==(const csDelegate<ArgType>& rhs) const;
	void operator=(const csDelegate<ArgType>& del);
	csDelegate(const csDelegate<ArgType>& del);

private:
	/// method pointer typedef
	typedef void (*StubType)(void*, ArgType);
	/// static method-call stub 
	template<class T, void(T::*method)(ArgType)> static void	MethodStub(void* objPtr, ArgType arg);
	/// static function-call stub
	template<void(*function)(ArgType)> static void				FunctionStub(void* dummyPtr, ArgType arg);

	void*		m_objPtr;
	StubType	m_stubPtr;
};

//------------------------------------------------------------------------------
template<class ArgType>
csDelegate<ArgType>::csDelegate() :
m_objPtr(0),
m_stubPtr(0)
{
	// empty
}

//------------------------------------------------------------------------------
template<class ArgType>
csDelegate<ArgType>::csDelegate(const csDelegate<ArgType>& del) :
m_objPtr(del.m_objPtr),
m_stubPtr(del.m_stubPtr)
{
	// empty
}

//------------------------------------------------------------------------------
template<class ArgType> void
csDelegate<ArgType>::operator()(ArgType arg) const
{
	if(0==m_stubPtr)
		return;
	(*this->m_stubPtr)(this->m_objPtr, arg);
}

//------------------------------------------------------------------------------
template<class ArgType>
template<class T, void (T::*method)(ArgType)>
static csDelegate<ArgType>
csDelegate<ArgType>::FromMethod(T* objPtr_)
{
	csDelegate<ArgType> del;
	del.m_objPtr = objPtr_;
	del.m_stubPtr = &MethodStub<T,method>;
	return del;
}

//------------------------------------------------------------------------------
template<class ArgType>
template<void(*function)(ArgType)>
static csDelegate<ArgType>
csDelegate<ArgType>::FromFunction()
{
	csDelegate<ArgType> del;
	del.m_objPtr = 0;
	del.m_stubPtr = &FunctionStub<function>;
	return del;
}

//------------------------------------------------------------------------------
template<class ArgType>
template<class T, void (T::*method)(ArgType)>
static void
csDelegate<ArgType>::MethodStub(void* objPtr_, ArgType arg_)
{
	T* obj = static_cast<T*>(objPtr_);
	(obj->*method)(arg_);
}

//------------------------------------------------------------------------------
template<class ArgType>
template<void(*function)(ArgType)>
static void
csDelegate<ArgType>::FunctionStub(void* dummyPtr, ArgType arg_)
{
	(*function)(arg_);
}

//------------------------------------------------------------------------------
template<typename ArgType> bool
csDelegate<ArgType>::operator==(const csDelegate<ArgType>& rhs) const
{
	if(this->m_objPtr!=rhs.m_objPtr)
		return false;
	return this->m_stubPtr==rhs.m_stubPtr; 
}

//------------------------------------------------------------------------------
template<class ArgType>
void 
csDelegate<ArgType>::operator=( const csDelegate<ArgType>& del )
{
	m_objPtr	= del.m_objPtr;
	m_stubPtr = del.m_stubPtr;
}

#endif